package test

import (
	"aigen-todo/internal/service"
	"aigen-todo/internal/models"
	"testing"
	"time"

	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

// MockUserRepository 是UserRepository的模拟实现
type MockUserRepository struct {
	mock.Mock
}

func (m *MockUserRepository) Create(user *models.User) error {
	args := m.Called(user)
	return args.Error(0)
}

func (m *MockUserRepository) GetByID(id uint64) (*models.User, error) {
	args := m.Called(id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*models.User), args.Error(1)
}

func (m *MockUserRepository) GetByUsername(username string) (*models.User, error) {
	args := m.Called(username)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*models.User), args.Error(1)
}

func (m *MockUserRepository) GetByEmail(email string) (*models.User, error) {
	args := m.Called(email)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*models.User), args.Error(1)
}

func (m *MockUserRepository) Update(user *models.User) error {
	args := m.Called(user)
	return args.Error(0)
}

func (m *MockUserRepository) Delete(id uint64) error {
	args := m.Called(id)
	return args.Error(0)
}

// TestUserService_Register 测试用户注册功能
func TestUserService_Register(t *testing.T) {
	// 创建模拟仓库
	mockRepo := new(MockUserRepository)
	userService := service.NewUserService(mockRepo)

	// 测试用例1：注册成功
	t.Run("RegisterSuccess", func(t *testing.T) {
		// 设置模拟返回值 - 用户不存在时返回错误
		mockRepo.On("GetByUsername", "testuser").Return(nil, gorm.ErrRecordNotFound)
		mockRepo.On("GetByEmail", "test@example.com").Return(nil, gorm.ErrRecordNotFound)
		mockRepo.On("Create", mock.AnythingOfType("*models.User")).Return(nil)

		// 执行测试
		user, err := userService.Register("testuser", "test@example.com", "password123")

		// 验证结果
		assert.NoError(t, err)
		assert.NotNil(t, user)
		assert.Equal(t, "testuser", user.Username)
		assert.Equal(t, "test@example.com", user.Email)
		mockRepo.AssertExpectations(t)
	})

	// 测试用例2：用户名已存在
	t.Run("RegisterUsernameExists", func(t *testing.T) {
		// 设置模拟返回值
		mockRepo.On("GetByUsername", "existinguser").Return(&models.User{}, nil)

		// 执行测试
		_, err := userService.Register("existinguser", "new@example.com", "password123")

		// 验证结果
		assert.Error(t, err)
		assert.Equal(t, service.ErrUserAlreadyExists, err)
		mockRepo.AssertExpectations(t)
	})

	// 测试用例3：邮箱已存在
	t.Run("RegisterEmailExists", func(t *testing.T) {
		// 设置模拟返回值
		mockRepo.On("GetByUsername", "newuser").Return(nil, gorm.ErrRecordNotFound)
		mockRepo.On("GetByEmail", "existing@example.com").Return(&models.User{
			ID:       1,
			Username: "existinguser",
			Email:    "existing@example.com",
		}, nil)

		// 执行测试
		_, err := userService.Register("newuser", "existing@example.com", "password123")

		// 验证结果
		assert.Error(t, err)
		assert.Equal(t, service.ErrUserAlreadyExists, err)
		mockRepo.AssertExpectations(t)
	})
}

// TestUserService_Login 测试用户登录功能
func TestUserService_Login(t *testing.T) {
	// 创建模拟仓库
	mockRepo := new(MockUserRepository)
	userService := service.NewUserService(mockRepo)

	// 测试用例1：登录成功
	t.Run("LoginSuccess", func(t *testing.T) {
		// 设置模拟返回值
		hashedPassword, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
		existingUser := &models.User{
			ID:        1,
			Username:  "testuser",
			Email:     "test@example.com",
			Password:  string(hashedPassword),
			CreatedAt: time.Now(),
		}
		mockRepo.On("GetByUsername", "testuser").Return(existingUser, nil)

		// 执行测试
		user, err := userService.Login("testuser", "password123")

		// 验证结果
		assert.NoError(t, err)
		assert.NotNil(t, user)
		assert.Equal(t, "testuser", user.Username)
		mockRepo.AssertExpectations(t)
	})

	// 测试用例2：用户不存在
	t.Run("LoginUserNotFound", func(t *testing.T) {
		// 设置模拟返回值
		mockRepo.On("GetByUsername", "nonexistent").Return(nil, gorm.ErrRecordNotFound)
		mockRepo.On("GetByEmail", "nonexistent").Return(nil, gorm.ErrRecordNotFound)

		// 执行测试
		_, err := userService.Login("nonexistent", "password123")

		// 验证结果
		assert.Error(t, err)
		assert.Equal(t, service.ErrInvalidCredentials, err)
		mockRepo.AssertExpectations(t)
	})

	// 测试用例3：密码错误
	t.Run("LoginInvalidPassword", func(t *testing.T) {
		// 设置模拟返回值
		hashedPassword, _ := bcrypt.GenerateFromPassword([]byte("correctpassword"), bcrypt.DefaultCost)
		existingUser := &models.User{
			ID:        1,
			Username:  "testuser",
			Password:  string(hashedPassword),
			CreatedAt: time.Now(),
		}
		mockRepo.On("GetByUsername", "testuser").Return(existingUser, nil)

		// 执行测试
		_, err := userService.Login("testuser", "wrongpassword")

		// 验证结果
		assert.Error(t, err)
		assert.Equal(t, service.ErrInvalidCredentials, err)
		mockRepo.AssertExpectations(t)
	})
}

// TestUserService_GetUserByID 测试通过ID获取用户功能
func TestUserService_GetUserByID(t *testing.T) {
	// 创建模拟仓库
	mockRepo := new(MockUserRepository)
	userService := service.NewUserService(mockRepo)

	// 测试用例1：获取成功
	t.Run("GetUserByIDSuccess", func(t *testing.T) {
		// 设置模拟返回值
		expectedUser := &models.User{
			ID:        1,
			Username:  "testuser",
			Email:     "test@example.com",
			CreatedAt: time.Now(),
		}
		mockRepo.On("GetByID", uint64(1)).Return(expectedUser, nil)

		// 执行测试
		user, err := userService.GetUserByID(uint64(1))

		// 验证结果
		assert.NoError(t, err)
		assert.NotNil(t, user)
		assert.Equal(t, uint64(1), user.ID)
		mockRepo.AssertExpectations(t)
	})

	// 测试用例2：用户不存在
	t.Run("GetUserByIDNotFound", func(t *testing.T) {
		// 设置模拟返回值 - 用户不存在时返回错误
		mockRepo.On("GetByID", uint64(999)).Return(nil, gorm.ErrRecordNotFound)

		// 执行测试
		_, err := userService.GetUserByID(uint64(999))

		// 验证结果
		assert.Error(t, err)
		mockRepo.AssertExpectations(t)
	})
}
